/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * RSA Cryptographic Device Class
 *      Provides a class for performing asymmetric encryption using the RSA algorithm.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.IO;
using System.Security.Cryptography;
using Adaptive.Foundation.IO;

namespace Adaptive.Foundation.Security
{
    /// <summary>
    /// Provides a class for performing asymmetric encryption using the RSA algorithm.
    /// </summary>
    public class RsaCryptoDevice : AsymmetricCryptographicDeviceBase
    {
        /*--------------------------------------------------------------------------------
         * Constructor / Dispose / Destructor Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public RsaCryptoDevice()
            : base()
        {
            SafeCreateRsa(null);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets a value indicating whether the current instance can perform
        /// decryption operations.
        /// </summary>
        /// <value>
        /// <b>true</b> if the current instance is in a state where decryption
        /// operations may be performed; otherwise, <b>false</b>.
        /// </value>
        public override bool CanDecrypt
        {
            get
            {
                return true;
            }
        }
        /// <summary>
        /// Gets a value indicating whether the current instance can perform
        /// encryption operations.
        /// </summary>
        /// <value>
        /// <b>true</b> if the current instance is in a state where encryption
        /// operations may be performed; otherwise, <b>false</b>.
        /// </value>
        public override bool CanEncrypt
        {
            get
            {
                return true;
            }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods / Event Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods / Event Methods
        /// <summary>
        /// Performs decryption of the supplied data.
        /// </summary>
        /// <param name="operation">
        /// A <see cref="CryptographicOperation"/> instance containing the neccessary
        /// information to perform the decryption.
        /// </param>
        /// <returns>
        /// <b>true</b> if the operation completes successfully; otherwise, <b>false</b>.
        /// </returns>
        protected override bool DoDecrypt(CryptographicOperation operation)
        {
            RSACryptoServiceProvider rsa = null;            //RSA decryption class.
            BinaryReader reader = null;                     //Data reader.
            BinaryWriter writer = null;                     //Data writer.
            VirtualMemoryStream outputStream = null;        //Output storage stream.
            bool returnValue = false;                       //Return value.
            byte[] original = null;                         //Original data content.
            byte[] encResult = null;                        //Decryption result container.

            if ((operation == null) || (operation.SourceData == null) || (operation.SourceData.Length == 0))
            {
                if (operation != null)
                {
                    operation.Result.LastException = new ArgumentNullException();
                    operation.Result.ResultType = CryptographicResultType.SourceDataException;
                    operation.Result.Success = false;
                }
            }
            else
            {
                //Create the RSA cryptographic object.
                rsa = SafeCreateRsa(operation);
                if (rsa != null)
                {
                    //Create the stream instances.
                    outputStream = new VirtualMemoryStream();
                    writer = new BinaryWriter(outputStream);
                    reader = new BinaryReader(operation.SourceData);

                    //Read data in 128 byte blocks until all blocks are read and decrypted or an 
                    //error occurs.
                    returnValue = true;
                    do
                    {
                        try
                        {
                            original = reader.ReadBytes(128);
                        }
                        catch (Exception ex)
                        {
                            operation.Result.Success = false;
                            operation.Result.ResultType = CryptographicResultType.SourceDataException;
                            operation.Result.LastException = ex;
                            original = null;
                            returnValue = false;
                        }
                        if ((original != null) && (original.Length > 0))
                        {
                            try
                            {
                                Array.Reverse(original);
                                encResult = rsa.Decrypt(original, false);
                                writer.Write(encResult);
                                Array.Clear(encResult, 0, encResult.Length);
                            }
                            catch (Exception ex)
                            {
                                operation.Result.LastException = ex;
                                operation.Result.ResultType = CryptographicResultType.CryptographicException;
                                operation.Result.Success = false;
                                original = null;
                                returnValue = false;
                            }
                        }

                    } while ((original != null) && (original.Length > 0));

                    //Clear.
                    rsa.Clear();
                    rsa = null;

                    if (returnValue)
                    {
                        writer.Flush();

                        operation.Result.Success = true;
                        operation.Result.ResultType = CryptographicResultType.Success;
                        operation.Result.ResultData = outputStream.Clone();
                    }
                    reader = null;

                    try
                    {
                        writer.Close();
                        outputStream.Close();
                        outputStream.Dispose();
                    }
                    catch
                    {
                    }
                }
            }

            return returnValue;
        }
        /// <summary>
        /// Performs encryption of the supplied data.
        /// </summary>
        /// <param name="operation">
        /// A <see cref="CryptographicOperation"/> instance containing the neccessary
        /// information to perform the encryption.
        /// </param>
        /// <returns>
        /// <b>true</b> if the operation completes successfully; otherwise, <b>false</b>.
        /// </returns>
        protected override bool DoEncrypt(CryptographicOperation operation)
        {       
            RSACryptoServiceProvider rsa = null;            //RSA encryption class.
            BinaryReader reader = null;                     //Data reader.
            BinaryWriter writer = null;                     //Data writer.
            VirtualMemoryStream outputStream = null;        //Output storage stream.
            bool returnValue = false;                       //Return value.
            byte[] original = null;                         //Original data content.
            byte[] encResult = null;                        //Encryption result container.

            if ((operation == null) || (operation.SourceData == null) || (operation.SourceData.Length == 0))
            {
                if (operation != null)
                {
                    operation.Result.LastException = new ArgumentNullException();
                    operation.Result.ResultType = CryptographicResultType.SourceDataException;
                    operation.Result.Success = false;
                }
            }
            else
            {
                //Create the RSA cryptographic object.
                if (rsa == null)
                    rsa = SafeCreateRsa(operation);

                if (rsa != null)
                {

                    //Create the stream instances.
                    outputStream = new VirtualMemoryStream();
                    writer = new BinaryWriter(outputStream);
                    reader = new BinaryReader(operation.SourceData);

                    //Read data in 64 byte blocks until all blocks are read and encrypted or an 
                    //error occurs.
                    returnValue = true;
                    do
                    {
                        try
                        {
                            original = reader.ReadBytes(64);
                        }
                        catch (Exception ex)
                        {
                            operation.Result.Success = false;
                            operation.Result.ResultType = CryptographicResultType.SourceDataException;
                            operation.Result.LastException = ex;
                            original = null;
                            returnValue = false;
                        }
                        if ((original != null) && (original.Length > 0))
                        {
                            try
                            {
                                encResult = rsa.Encrypt(original, false);
                                Array.Reverse(encResult);
                                writer.Write(encResult);
                                Array.Clear(encResult, 0, encResult.Length);
                            }
                            catch (Exception ex)
                            {
                                operation.Result.LastException = ex;
                                operation.Result.ResultType = CryptographicResultType.CryptographicException;
                                operation.Result.Success = false;
                                original = null;
                                returnValue = false;
                            }
                        }

                    } while ((original != null) && (original.Length > 0));

                    //Clear.
                    rsa.Clear();
                    rsa = null;

                    if (returnValue)
                    {
                        writer.Flush();

                        operation.Result.Success = true;
                        operation.Result.ResultType = CryptographicResultType.Success;
                        operation.Result.ResultData = outputStream.Clone();
                    }
                    reader = null;

                    try
                    {
                        writer.Close();
                        outputStream.Close();
                        outputStream.Dispose();
                    }
                    catch
                    {
                    }
                }
            }

            return returnValue;
        }
        /// <summary>
        /// Validates the specified encryption key for use by the current
        /// implementation.
        /// </summary>
        /// <param name="keyData">
        /// A byte array containing the data to be validated.
        /// </param>
        /// <returns>
        /// <b>true</b> if the key data is valid; otherwise, <b>false</b>.
        /// </returns>
        protected override bool ValidateKey(byte[] keyData)
        {
            return true;
        }
        /// <summary>
        /// Sets the type of algorithm used by this implementation.
        /// </summary>
        protected override void SetAlgorithmType()
        {
            base.Algorithm = CryptographicAlgorithm.Rsa;
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Private Methods / Functions
        /// <summary>
        /// Creates the local RSA cryptographic provider instance.
        /// </summary>
        /// <returns></returns>
        private CspParameters CreateProvider()
        {
            CspParameters cspParams = null;

            cspParams = new CspParameters();
            cspParams.KeyContainerName = "Adaptive.Intelligence";
            return cspParams;
        }
        /// <summary>
        /// Attempts to create an RSA provider instance.
        /// </summary>
        /// <param name="operation">
        /// The <see cref="CryptographicOperation"/> instance to be performed.
        /// </param>
        /// <returns>
        /// An <see cref="RSACryptoServiceProvider"/> instance if successful; otherwise, returns <b>null</b>.
        /// </returns>
        private RSACryptoServiceProvider SafeCreateRsa(CryptographicOperation operation)
        {
            RSACryptoServiceProvider returnValue = null;
            RSAParameters rsaParams;

            try
            {
                returnValue = new RSACryptoServiceProvider(1024, CreateProvider());
                returnValue.PersistKeyInCsp = true;
                rsaParams = returnValue.ExportParameters(false);
                Key = rsaParams.Modulus;

            }
            catch (Exception ex)
            {
                if (operation != null)
                {
                    operation.Result.LastException = ex;
                    operation.Result.ResultType = CryptographicResultType.UnknownException;
                    operation.Result.Success = false;
                }
                returnValue = null;
            }

            return returnValue;
        }
        #endregion

    }
}
